package com.after.saas.common.utils;

import com.after.saas.common.converter.LocalDateTimeConverter;
import com.after.saas.common.exception.BaseException;
import com.after.saas.common.model.DynamicTitle;
import com.after.saas.common.model.bo.CoreDictBO;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.springframework.http.MediaType;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Classname EasyExcelUtil
 * @Description TODO
 * @Date 2021/3/25 8:50
 * @Created by hjchen
 */

@Slf4j
public class EasyExcelUtil {

    private final static HorizontalCellStyleStrategy HORIZONTAL_CELL_STYLE_STRATEGY;

    static {
        // 头的策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        WriteFont headWriteFont = new WriteFont();
        headWriteFont.setFontHeightInPoints((short) 12);
        headWriteCellStyle.setWriteFont(headWriteFont);
        // 内容的策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);
        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);
        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);
        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        // 内容字体
        WriteFont contentWriteFont = new WriteFont();
        // 字体大小
        contentWriteFont.setFontHeightInPoints((short) 11);
        contentWriteCellStyle.setWriteFont(contentWriteFont);
        // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
        HORIZONTAL_CELL_STYLE_STRATEGY = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
    }

    public static void export(HttpServletResponse response, List<?> data, Class<?> clazz) {
        export(response, data, clazz, "");
    }

    /**
     * 导出
     *
     * @param response
     **/
    public static void export(HttpServletResponse response, List<?> data, Class<?> clazz, String name) {
        String fileName = LocalDateUtil.formatDateTime(LocalDateTime.now(), LocalDateUtil.TIME_FORMATTER_2);
        if (StringUtils.isNotEmpty(name)) fileName = String.format("%s_%s", fileName, name);
        try {
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
            EasyExcel.write(response.getOutputStream(), clazz)
                    .registerConverter(new LocalDateTimeConverter())
                    .registerWriteHandler(HORIZONTAL_CELL_STYLE_STRATEGY)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .autoCloseStream(true)
                    .sheet(fileName).doWrite(data);
        } catch (Exception e) {
            log.error("message {} cause {}", e.getMessage(), e.getCause());
            // 重置response
            response.reset();
            throw new BaseException("下载失败");
        }
    }


    /**
     * 导出
     *
     * @param response
     **/
    public static void export(HttpServletResponse response, List<?> data, List<String> headList, Class<?> clazz, String name) {
        String fileName = LocalDateUtil.formatDateTime(LocalDateTime.now(), LocalDateUtil.TIME_FORMATTER_2);
        if (StringUtils.isNotEmpty(name)) fileName = String.format("%s_%s", fileName, name);
        try {
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
            EasyExcel.write(response.getOutputStream())
                    .head(buildHead(headList))
                    .registerConverter(new LocalDateTimeConverter())
                    .registerWriteHandler(HORIZONTAL_CELL_STYLE_STRATEGY)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .sheet(fileName).doWrite(data);
        } catch (Exception e) {
            log.error("message {} cause {}", e.getMessage(), e.getCause());
            // 重置response
            response.reset();
            throw new BaseException("下载失败");
        }
    }

    /**
     * 导出
     *
     * @param response
     **/
    public static <T> void export(HttpServletResponse response, List<T> data, List<DynamicTitle> headList, String name) {
        String fileName = LocalDateUtil.formatDateTime(LocalDateTime.now(), LocalDateUtil.TIME_FORMATTER_2);
        if (StringUtils.isNotEmpty(name)) fileName = String.format("%s_%s", fileName, name);
        try {
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
            EasyExcel.write(response.getOutputStream())
                    .head(dynamicHead(headList))
                    .registerConverter(new LocalDateTimeConverter())
                    .registerWriteHandler(HORIZONTAL_CELL_STYLE_STRATEGY)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .sheet(fileName).doWrite(data);
        } catch (Exception e) {
            log.error("message {} cause {}", e.getMessage(), e.getCause());
            // 重置response
            response.reset();
            throw new BaseException("下载失败");
        }
    }

    /**
     * 导出
     *
     * @param filePath
     **/
    public static <T> void exportToFile(String filePath, List<T> data, List<DynamicTitle> headList, String name) {
        String fileName = LocalDateUtil.formatDateTime(LocalDateTime.now(), LocalDateUtil.TIME_FORMATTER_2);
        if (StringUtils.isNotEmpty(name)) fileName = String.format("%s_%s", fileName, name);
        try {
            EasyExcel.write(new File(filePath, fileName))
                    .head(dynamicHead(headList))
                    .registerConverter(new LocalDateTimeConverter())
                    .registerWriteHandler(HORIZONTAL_CELL_STYLE_STRATEGY)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .sheet(fileName).doWrite(buildData(headList, data));
        } catch (Exception e) {
            log.error("message {} cause {}", e.getMessage(), e.getCause());
            // 重置response
            throw new BaseException("下载失败");
        }
    }

    /**
     * 导出
     *
     * @param response
     **/
    public static void exportMultiToWeb(HttpServletResponse response, List<?> data, List<String> headList, String name) {
        String fileName = LocalDateUtil.formatDateTime(LocalDateTime.now(), LocalDateUtil.TIME_FORMATTER_2);
        if (StringUtils.isNotEmpty(name)) fileName = String.format("%s_%s", fileName, name);
        try {
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
            ExcelWriter excelWriter = null;
            excelWriter = EasyExcel.write(response.getOutputStream()).registerConverter(new LocalDateTimeConverter()).build();
            for (int i = 0; i < 2; i++) {
                if (!CollectionUtils.isEmpty(data)) {
                    //这里 需要指定写用哪个class去写
                    WriteSheet writeSheet = EasyExcel.writerSheet(i, "sheet" + i).head(buildHead(headList)).build();
                    excelWriter.write(data, writeSheet);
                }
            }
            //千万别忘记finish 会帮忙关闭流
            excelWriter.finish();
        } catch (Exception e) {
            throw new BaseException("下载失败");
        }
    }

    public static void exportMultiToFile(String filePath, List<?> data, Class<?> clazz, List<String> headList) {
        String fileName = LocalDateUtil.formatDateTime(LocalDateTime.now(), LocalDateUtil.TIME_FORMATTER_2) + ".xlsx";
        try {
            ExcelWriter excelWriter = EasyExcel.write(new File(filePath + File.separator + fileName))
                    .registerConverter(new LocalDateTimeConverter())
                    .registerWriteHandler(HORIZONTAL_CELL_STYLE_STRATEGY)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .build();
            for (int j = 0; j < 2; j++) {
                if (j == 0) {
                    //这里 需要指定写用哪个class去写
                    WriteSheet writeSheet = EasyExcel.writerSheet(j, "sheet" + j).head(buildHead(headList)).build();
                    excelWriter.write(data, writeSheet);
                }
                if (j == 1) {
                    //这里 需要指定写用哪个class去写
                    WriteSheet writeSheet = EasyExcel.writerSheet(j, "sheet" + j).head(clazz).build();
                    excelWriter.write(data, writeSheet);
                }
            }
            //千万别忘记finish 会帮忙关闭流
            excelWriter.finish();
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException("下载失败");
        }
    }

    private static List<List<String>> buildHead(List<String> ab) {
        return ab.stream().flatMap(head -> Stream.of(Collections.singletonList(head))).collect(Collectors.toList());
    }


    private static List<List<String>> dynamicHead(List<DynamicTitle> customFile) {
        List<String> ab = customFile.stream().flatMap(batch -> Stream.of(batch.getName())).collect(Collectors.toList());
        return ab.stream().flatMap(head -> Stream.of(Collections.singletonList(head))).collect(Collectors.toList());
    }

    public static <T> List<List<Object>> buildData(List<DynamicTitle> customFiled, List<T> batchList) {
        List<List<Object>> dataList = new ArrayList<>();
        for (T template : batchList) {
            List<Object> list = new ArrayList<>();
            for (DynamicTitle field : customFiled) {
                try {
                    PropertyDescriptor descriptor = new PropertyDescriptor(field.getCode(), template.getClass());
                    Method readMethod = descriptor.getReadMethod();
                    Object o;
                    if (StringUtils.equals(readMethod.getName(), "getStatus")) {
                        o = readMethod.invoke(template);
                        System.out.println("readMethod=" + o);
                    } else {
                        o = readMethod.invoke(template);
                    }
                    list.add(o);
                } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
                    log.error("error {}", e.getMessage());
                }
            }
            dataList.add(list);
        }
        return dataList;
    }

    public static void main(String[] args) {
        List<List<Object>> dataList = new ArrayList<>();
        List<DynamicTitle> titles = new ArrayList<>();
        DynamicTitle dynamicTitle1 = new DynamicTitle();
        dynamicTitle1.setCode("dictSort");
        dynamicTitle1.setName("字典排序");
        titles.add(dynamicTitle1);
        DynamicTitle dynamicTitle2 = new DynamicTitle();
        dynamicTitle2.setCode("dictType");
        dynamicTitle2.setName("字典类型");
        titles.add(dynamicTitle2);
        DynamicTitle dynamicTitle3 = new DynamicTitle();
        dynamicTitle3.setCode("dictLabel");
        dynamicTitle3.setName("字典标签");
        titles.add(dynamicTitle3);
        DynamicTitle dynamicTitle4 = new DynamicTitle();
        dynamicTitle4.setCode("dictValue");
        dynamicTitle4.setName("字典键值");
        titles.add(dynamicTitle4);
        List<CoreDictBO> dictList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            CoreDictBO dictBO = new CoreDictBO();
            dictBO.setDictSort(i);
            dictBO.setDictType("0" + i);
            dictBO.setDictValue("测试" + i);
            dictBO.setDictLabel("test" + i);
            dictList.add(dictBO);
            List<Object> list = new ArrayList<>();
            list.add("Sort" + i);
            list.add("DictType" + (i + 1));
            list.add("DictValue" + i);
            list.add("DictLabel" + i);
            list.add(LocalDateTime.now());
            dataList.add(list);
        }
        String filePath = "d:\\";
        exportToFile(filePath, dictList, titles, "测试动态导出表头.xlsx");
//        exportMultiToFile(filePath, dataList, CoreDictBO.class, Arrays.asList("字典类型", "备注信息", "字典排序", "字典键值", "备注信息"));
    }

}
